package com.yunyun400.vosk.service;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.freeswitch.esl.client.IEslEventListener;
import org.freeswitch.esl.client.inbound.Client;
import org.freeswitch.esl.client.transport.event.EslEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * ESL服务类，负责处理FreeSWITCH的外呼操作
 */
@Service
public class EslService {
    
    private static final Logger logger = LoggerFactory.getLogger(EslService.class);
    
    @Autowired
    private ReminderService reminderService;
    
    // FreeSWITCH ESL连接信息
    @Value("${freeswitch.host:47.76.231.78}")
    private String FS_HOST;  // FS服务器IP
    
    @Value("${freeswitch.port:8021}")
    private int FS_PORT;     // ESL默认端口
    
    @Value("${freeswitch.password:!QAZ2wsxyunyun400.COM}")
    private String FS_PASSWORD; // ESL默认密码
    
    @Value("${freeswitch.callerid:1000}")
    private String CALLER_ID; // 主叫号码
    
    @Value("${recording.storage.path:}")
    private String RECORDING_STORAGE_PATH; // 录音存储目录
    
    /**
     * 执行批量外呼操作
     */
    public void performBatchCalls() throws Exception {
        logger.info("开始批量外呼任务");
        
        // 获取需要外呼的号码列表
        List<String> phoneNumbers = getPhoneNumbersToCall();
        logger.info("获取到 {} 个需要外呼的号码", phoneNumbers.size());
        
        if (phoneNumbers.isEmpty()) {
            logger.info("没有需要外呼的号码，任务结束");
            return;
        }
        
        // 创建ESL客户端
        Client client = new Client();
        
        try {
            // 连接到FreeSWITCH ESL
            client.connect(FS_HOST, FS_PORT, FS_PASSWORD, 10);
            logger.info("成功连接到FreeSWITCH ESL");
            // 订阅挂断事件并设置事件监听器
            client.setEventSubscriptions("plain", "CHANNEL_HANGUP_COMPLETE");
            final BlockingQueue<String> hangupQueue = new LinkedBlockingQueue<>();
            client.addEventListener(new IEslEventListener() {
                @Override
                public void eventReceived(EslEvent event) {
                    if ("CHANNEL_HANGUP_COMPLETE".equals(event.getEventName())) {
                        String dest = event.getEventHeaders().get("Caller-Destination-Number");
                        if (dest == null) {
                            dest = event.getEventHeaders().get("variable_sip_to_user");
                        }
                        if (dest != null) {
                            hangupQueue.offer(dest);
                        }
                    }
                }
                @Override
                public void backgroundJobResultReceived(EslEvent event) {
                    // no-op
                }
            });
            
            // 逐个外呼号码
            for (String calleeNumber : phoneNumbers) {
                try {
                    logger.info("开始外呼号码: {}", calleeNumber);
                    makeCall(client, calleeNumber);
                    // 等待上一通电话挂断，再进行下一通
                    boolean hungUp = waitForHangup(hangupQueue, calleeNumber, 180);
                    if (!hungUp) {
                        logger.warn("号码 {} 在超时时间内未收到挂断事件，继续下一通", calleeNumber);
                    }
                } catch (Exception e) {
                    logger.error("外呼号码 {} 失败", calleeNumber, e);
                    // 继续外呼下一个号码
                    continue;
                }
            }
            // 断开ESL连接
            client.close();
            logger.info("已断开FreeSWITCH ESL连接");
            
            // 外呼完成后，延迟1分钟开始录音识别
            try {
                logger.info("外呼任务完成，30秒后开始处理录音文件");
                TimeUnit.SECONDS.sleep(30);
                
                // 生成批次ID
                String batchId = UUID.randomUUID().toString();
                logger.info("开始处理录音文件，批次ID: {}", batchId);
                
                // 执行录音检查并创建提醒
                reminderService.checkRecordingsAndCreateReminders(batchId);
                
                // 删除已处理的录音文件
                deleteProcessedRecordingFiles();
            } catch (InterruptedException e) {
                logger.error("等待录音处理时被中断", e);
                Thread.currentThread().interrupt();
            }
        } catch (Exception e) {
            logger.error("批量外呼过程中发生错误", e);
            throw e;
        } finally {
            if(client != null && client.canSend()) {
            	client.close();
            }
        }
    }
    
    /**
     * 等待对应被叫号码的挂断事件
     */
    private boolean waitForHangup(BlockingQueue<String> hangupQueue, String calleeNumber, long timeoutSeconds) {
        long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds);
        while (System.nanoTime() < deadline) {
            long remainingMillis = TimeUnit.NANOSECONDS.toMillis(deadline - System.nanoTime());
            if (remainingMillis <= 0) {
                break;
            }
            try {
                String num = hangupQueue.poll(Math.min(remainingMillis, 1000), TimeUnit.MILLISECONDS);
                if (num == null) {
                    continue;
                }
                if (calleeNumber.equals(num)) {
                    return true;
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
        return false;
    }
    
    /**
     * 执行单个外呼
     */
    private void makeCall(Client client, String calleeNumber) throws Exception {
        // 构建拨号命令
        String dialString = String.format(
                "originate {param1=%s,caller_id_number=%s,originate_timeout=20}[execute_on_answer='sched_hangup +25']user/%s %s",
                calleeNumber, CALLER_ID, CALLER_ID, calleeNumber
            );
        
        // 发送拨号命令并获取结果
        String uuid = client.sendAsyncApiCommand(dialString, "");
        logger.info("外呼命令已发送，会话ID: {}, 被叫号码: {}", uuid, calleeNumber);
    }
    
    /**
     * 获取需要外呼的号码列表
     * 这里可以从数据库、文件或其他数据源获取
     */
    private List<String> getPhoneNumbersToCall() {
        // 示例：返回模拟的号码列表（合并为循环生成）
        // 实际项目中应从数据库或配置文件中获取
        List<String> phoneNumbers = new ArrayList<>();
        for(int j = 1; j <= 5; j++) {
            for (int i = 1; i <= 32; i++) {
                phoneNumbers.add(String.format("88889%d%02d639285046674", j,i));
            }
        }
        return phoneNumbers;
    }
    
    /**
     * 删除录音存储目录下的所有文件（忽略传入列表，以配置为准）
     */
    private void deleteProcessedRecordingFiles() {
        if (RECORDING_STORAGE_PATH == null || RECORDING_STORAGE_PATH.trim().isEmpty()) {
            logger.warn("recording.storage.path 未配置，跳过删除录音文件");
            return;
        }
        
        File dir = new File(RECORDING_STORAGE_PATH);
        if (!dir.exists() || !dir.isDirectory()) {
            logger.warn("录音目录不存在或不是目录: {}", RECORDING_STORAGE_PATH);
            return;
        }
        
        File[] files = dir.listFiles();
        if (files == null || files.length == 0) {
            logger.info("录音目录无文件可删: {}", RECORDING_STORAGE_PATH);
            return;
        }
        
        int deletedCount = 0;
        int failedCount = 0;
        
        for (File f : files) {
            try {
                if (deleteRecursively(f)) {
                    logger.info("成功删除录音: {}", f.getAbsolutePath());
                    deletedCount++;
                } else {
                    logger.warn("删除失败: {}", f.getAbsolutePath());
                    failedCount++;
                }
            } catch (Exception e) {
                logger.error("删除录音 {} 时发生错误", f.getAbsolutePath(), e);
                failedCount++;
            }
        }
        
        logger.info("录音目录清理完成: 成功 {}，失败 {}，总计 {}", deletedCount, failedCount, files.length);
    }
    
    /**
     * 递归删除文件/目录
     */
    private boolean deleteRecursively(File file) {
        if (file.isDirectory()) {
            File[] children = file.listFiles();
            if (children != null) {
                for (File child : children) {
                    deleteRecursively(child);
                }
            }
        }
        return file.delete();
    }
}